package com.zyj.retry.service;

import com.zyj.retry.dto.Dog;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

@Service
public class RetryService {

    public Dog doWrongOnce() {
        System.out.println("doWrongOnce");
        throw new RuntimeException("doWrongOnce exception");
    }

    @Retryable(
            maxAttempts = 2,  //方法抛出异常会尝试重试5次
            backoff = @Backoff(100), //每次重试间隔100ms
            include = RuntimeException.class, //当触发这种类型的异常才会重试, 如果不写就是所有异常
            exclude = Error.class, //当触发这种类型的异常, 不会进行重试
            recover = "recover123" //注解中如果指定了recover的方法, 则recover的方法入参必须与本方法一致, 并且要在第一个参数前加上对应的Exception
    )
    public Dog doWrong(String name, Integer age) {
        System.out.println("doWrong");
        throw new RuntimeException("doWrong exception");
    }

    //如果有@Recover在重试超过次数之后, 就会转而调用@Recover, 而不是继续向外抛出异常

    /**
     * 如果 @Retryable 中没指定 recover, 则会自动匹配到这里, 但要保证
     * 1. 第一个参数是对应的异常
     * 2. recover方法的形参名字不做规定, 但类型有限制
     * 3. 第一个参数后面的参数顺序可以少于原方法的参数, 但是要求参数顺序一定要保持一致
     * 4. 返回值与原方法保持一致
     * 例如: 原方法参数(String a, Integer b);
     * 那这里recover方法定义如下
     * 可以是(RuntimeException e)
     * 可以是(RuntimeException e, String asd)
     * 可以是(RuntimeException e, String dsa, Integer qwe)
     * ---
     * 如果 @Retryable 中指定了 recover属性, 那么必须保证
     * 1. recover方法的名字, 与 @Retryable 中指定的 recover属性 一致
     * 2. recover方法的第一个入参必须是异常
     * 3. recover方法的形参名字不做规定, 但类型有限制
     * 4. 返回值与原方法保持一致
     * 5. ☆ recover方法的其他入参类型和数量必须与元函数保持一致
     * 例如: 原方法参数(String a, Integer b);
     * recover方法参数必须是: (RuntimeException e, String dsa, Integer qwe)
     */
    @Recover //标记了该注解的, 才被任务是recover方法
    public Dog recover123(RuntimeException e, String aa, Integer dsa) {
        System.out.println("recover " + e.getMessage());
        Dog dog = new Dog();
        dog.setName(aa);
        dog.setAge(dsa);
        return dog;
    }

}
